package com.kehutong.api.service;

import java.io.IOException;
import java.io.OutputStream;
import java.net.URLEncoder;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.kehutong.common.Authutil.OAuth2Provider;
import com.kehutong.common.DoveClient;
import com.kehutong.common.entity.OAuthClient;
import org.coraframework.authz.AccessToken;
import org.coraframework.authz.SubjectUtil;
import org.coraframework.cache.Cache;
import org.coraframework.cache.support.FIFOCache;
import org.coraframework.inject.Inject;
import org.coraframework.inject.Singleton;
import org.coraframework.json.JSONObject;
import org.coraframework.logger.Logger;
import org.coraframework.logger.LoggerFactory;
import org.coraframework.oauth2.OAuth2;
import org.coraframework.oauth2.OAuth2Accessor;
import org.coraframework.oauth2.OAuth2Client;
import org.coraframework.oauth2.OAuth2ProblemException;
import org.coraframework.oauth2.code.ErrorCode;
import org.coraframework.oauth2.code.ResponseType;
import org.coraframework.orm.Pool;

import org.coraframework.orm.jdbc.JdbcSession;
import org.coraframework.util.Objects;
import org.coraframework.util.UUIDGenerator;
import org.coraframework.util.time.TimeUtil;


import com.kehutong.common.urlconfig.FileUrlConfig;
import com.kehutong.common.urlconfig.OpenUrlConfig;

@Singleton
public class Oauth2Service {

	private static final Logger logger = LoggerFactory.getLogger(Oauth2Service.class);

	@Inject
	private OAuth2Provider oauth2Provider;
	@Inject
	private JdbcSession SignJDBCSession;
	@Inject
	private FileUrlConfig fileUrlConfig;
	@Inject
	private OpenUrlConfig openUrlConfig;


	@Inject
	private DoveClient doveClient;

	private Cache<String, JSONObject> cacheMap;

	Oauth2Service() {
		this.cacheMap = new FIFOCache<>("oauth2-cache", 10 * 1024 * 1024, 10 * TimeUtil.SECONDS_PER_MINUTE);
	}

	public void authorization(HttpServletRequest request, HttpServletResponse response)
	throws IOException, ServletException {
		try {
			logger.debug("request:{}", request.getQueryString());
			AccessToken accessToken = SubjectUtil.getSubject().getToken();
			logger.debug("accessToken:{}", accessToken);
			if (Objects.isNull(accessToken)) {
				tenchentOauth2(request, response);
				return;
			}
			JSONObject sessionResult = doveClient.post("/kht-bin/session/service/get", http->http.addParam("sessionId", accessToken.getSessonId()));
			if (sessionResult.getIntValue("code") != 200) {
				throw new NullPointerException("Oauth2Service.authorization get session 远程服务调用错误:" + sessionResult.toJSONString());
			}
			JSONObject session=sessionResult.getJSONObject("data");
			if(Objects.isNull(session)){
				throw new NullPointerException("Oauth2Service.authorization 获取Session 信息为空");
			}
			String uuid=session.getString("uuid");
			String userId=session.getString("userId");
			String corpid=session.getString("corpid");
			String extContent=session.getString("extContent");

			final String appId = request.getParameter(OAuth2.APPID);
			OAuth2Client client = oauth2Provider.getOAuth2ClientByAppId(appId);
			OAuth2Accessor accessor = new OAuth2Accessor(client);
			logger.debug("session:{}", session);

			OAuth2Provider.markAsAuthorized(accessor, uuid,userId,corpid,extContent);
			//OAuth2Provider.markAsAuthorized(accessor, userId);
			String requested = request.getParameter(OAuth2.RESPONSE_TYPE);

			if (requested.equals(ResponseType.CODE)) {
				oauth2Provider.generateCode(accessor);
				request.setAttribute(OAuth2.STATE, request.getParameter(OAuth2.STATE));
				returnToConsumer(null, request, response, accessor);
			} else {
				throw new OAuth2ProblemException(ErrorCode.UNSUPPORTED_RESPONSE_TYPE);
			}
		} catch (Exception e) {
			handleException(e, request, response);
		}
	}

	public void tencentCallback(JSONObject json, HttpServletRequest request, HttpServletResponse response)
			throws IOException, ServletException {
		try {
			String randomId = json.getString("randomId");
			JSONObject receiver = cacheMap.get(randomId);
			final String userId = SubjectUtil.getSubject().getToken().getUserId();
			OAuth2Client client = oauth2Provider.getOAuth2ClientByAppId(receiver.get("appId").toString());
			OAuth2Accessor accessor = new OAuth2Accessor(client);
			OAuth2Provider.markAsAuthorized(accessor, userId);
			oauth2Provider.generateCode(accessor);
			returnToConsumer(receiver.get("redirectUrl").toString(), request, response, accessor);
		} catch (Exception e) {
			handleException(e, request, response);
		}
	}

	public void test() throws Exception {


		fileUrlConfig.post("", http -> {
			String temp = URLEncoder.encode("key=v1&key1=v2") ;
			http.setBody(temp);
		});
	}

	public void getAccess_token(String appId, String secret, HttpServletRequest request, HttpServletResponse response)
			throws IOException, ServletException {
		try {
			final OAuth2Accessor accessor = oauth2Provider.getAccessorByAppId(appId);

			if (!accessor.getClient().getSecret().equals(secret)) {
				throw new OAuth2ProblemException(ErrorCode.INVALID_CLIENT);
			}

			oauth2Provider.generateAccessToken(accessor);

			response.setContentType("application/json");
			OutputStream out = response.getOutputStream();

			if (accessor.getRefreshToken() != null) {
				OAuth2.formEncodeInJson(OAuth2.newList(OAuth2.ACCESS_TOKEN, accessor.getAccessToken(),
						OAuth2.EXPIRES_IN, accessor.getExpires_in(), OAuth2.REFRESH_TOKEN, accessor.getRefreshToken()),
						out);
			} else {
				OAuth2.formEncodeInJson(OAuth2.newList(OAuth2.ACCESS_TOKEN, accessor.getAccessToken(),
						OAuth2.EXPIRES_IN, accessor.getExpires_in()), out);
			}
			out.flush();
			out.close();

		} catch (Exception e) {
			Boolean sendBodyInJson = true;
			Boolean withAuthHeader = false;
			OAuth2Provider.handleException(e, request, response, sendBodyInJson, withAuthHeader);
		}
	}

	public String getSecret(String companyNo){
		OAuthClient oAuthClient = SignJDBCSession.findOne(OAuthClient.class)
											.eq("companyNo", companyNo)
											.eq("deleted", false)
											.exe();
		if (Objects.nonNull(oAuthClient)) {
			return oAuthClient.getSecret();
		}
		return null;
	}

	private void handleException(Exception e, HttpServletRequest request, HttpServletResponse response)
			throws IOException, ServletException {
		Boolean sendBodyInJson = false;
		Boolean withAuthHeader = false;
		if (e instanceof OAuth2ProblemException) {
			OAuth2ProblemException problem = (OAuth2ProblemException) e;
			problem.setParameter(OAuth2ProblemException.HTTP_STATUS_CODE, new Integer(302));
		}
		OAuth2Provider.handleException(e, request, response, sendBodyInJson, withAuthHeader);
	}

	private void returnToConsumer(String redirectUrl, HttpServletRequest request, HttpServletResponse response,
			OAuth2Accessor accessor) throws IOException, ServletException {
		// send the user back to site's callBackUrl
		String redirect_uri = "";
		if (null == redirectUrl) {
			redirect_uri = request.getParameter(OAuth2.REDIRECT_URI);
		} else {
			redirect_uri = redirectUrl;
		}
		final StringBuilder buf = new StringBuilder();
		buf.append(redirect_uri);
		if (null != redirect_uri && redirect_uri.indexOf('?') != -1) {
			buf.append("&");
		} else {
			buf.append("?");
		}
		buf.append(OAuth2.CODE).append("=").append(accessor.getCode());
		final String state = request.getParameter(OAuth2.STATE);
		if (Objects.nonEmpty(state) && !OAuth2.STATE.equals(state)) {
			buf.append("&").append(OAuth2.STATE).append("=").append(state);
		}
		accessor.setState(state);
		logger.debug("{}", buf.toString());

		response.setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY);
		response.setHeader("Location", OAuth2.decodePercent(buf.toString()));
	}

	/**
	 * 腾讯oauthor2.0授权
	 * @param request
	 * @param response
	 * @throws IOException
	 */
	private void tenchentOauth2(HttpServletRequest request, HttpServletResponse response) throws IOException {
		String state = request.getParameter(OAuth2.STATE);
		String redirectUrl = request.getParameter(OAuth2.REDIRECT_URI);
		String randomId = UUIDGenerator.randomUUID();
		String appId = request.getParameter(OAuth2.APPID);

		OAuthClient oauthClient = Pool.get(OAuthClient.class, state);

		JSONObject json = new JSONObject();
		json.put("redirectUrl", redirectUrl);
		json.put("randomId", randomId);
		json.put("appId", appId);
		cacheMap.put(randomId, json);

		StringBuilder buf = new StringBuilder(openUrlConfig.getUrl("/connect/oauth2/authorize?"))
				.append("appId").append("=").append(appId)
				.append("&").append(OAuth2.REDIRECT_URI).append("=").append(fileUrlConfig.getUrl()).append("/app/oauth2/callback/").append(randomId)
				.append("&").append(OAuth2.RESPONSE_TYPE).append("=").append(OAuth2.CODE)
				.append("&").append(OAuth2.SCOPE).append("=").append("snsapi_base")
				.append("&").append(OAuth2.STATE).append("=").append(appId)
				.append("#wechat_redirect");

		// 跳转到腾讯2.0认证地址
		response.sendRedirect(OAuth2.decodePercent(buf.toString()));
	}

}
